﻿using Dubbo.Common;
using Dubbo.Common.Extension;
using Dubbo.Common.Utils;
using Dubbo.Rpc;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dubbo.Config.Api
{
    public class ReferenceConfig<T> : AbstractReferenceConfig
        //where T : class
    {
        //private static readonly IProtocol refprotocol;   // = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

        //private static readonly Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

        //private static readonly IProxyFactory proxyFactory;  //= ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

        private IProtocol refprotocol;
        private IProxyFactory proxyFactory;

        // 接口类型
        private string interfaceName;
        public string Interface
        {
            get
            {
                return string.IsNullOrEmpty(interfaceName) ? typeof(T).Name : interfaceName;
            }
            set
            {
                this.interfaceName = value;
            }
        }

        //private Type interfaceClass;

        // 客户端类型
        private string client;

        // 点对点直连服务提供地址
        public string Url { get; set; }

        // 方法配置
        //private List<MethodConfig> methods;

        // 缺省配置
        //private ConsumerConfig consumer;

        public string Protocol { get; set; }

        // 接口代理类引用
        //private volatile T impl;
        private T impl;

        private volatile IInvoker<T> invoker;

        private volatile bool initialized;

        private volatile bool destroyed;

        private readonly List<URL> urls = new List<URL>();


        public URL ToUrl()
        {
            return urls == null || urls.Count == 0 ? null : urls[0];
        }

        public List<URL> ToUrls()
        {
            return urls;
        }

        private object obj = new object();

        public T Get()
        {
            lock (obj)
            {
                if (destroyed)
                {
                    throw new InvalidOperationException("Already destroyed!");
                }
                if (impl == null)
                {
                    Init();
                }
                return impl;
            }
        }
        
        public void Destroy()
        {
            lock (obj)
            {
                if (impl == null)
                {
                    return;
                }
                if (destroyed)
                {
                    return;
                }
                destroyed = true;
                try
                {
                    invoker.Destroy();
                }
                catch (Exception t)
                {
                    //logger.warn("Unexpected err when destroy invoker of ReferenceConfig(" + url + ").", t);
                }
                invoker = null;
                impl = default(T);
            }
        }

        private void Init()
        {
            if (initialized)
            {
                return;
            }
            initialized = true;
            if (string.IsNullOrEmpty(Interface))
            {
                throw new Exception("<dubbo:reference interface=\"\" /> interface not allow null!");
            }
            // 获取消费者全局配置
            //checkDefault();
            //appendProperties(this);

            CheckApplication();
            //checkStubAndMock(interfaceClass);
            var dict = new Dictionary<string, string>();

            impl = CreateProxy(dict);
        }

        private T CreateProxy(Dictionary<string, string> dict)
        {
            URL tmpUrl = new URL("temp", "localhost", 0, dict);
            bool isJvmRefer = false;
            //if (isInjvm() == null)
            //{
            //    if (url != null && url.length() > 0)
            //    { //指定URL的情况下，不做本地引用
            //        isJvmRefer = false;
            //    }
            //    else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl))
            //    {
            //        //默认情况下如果本地有服务暴露，则引用本地服务.
            //        isJvmRefer = true;
            //    }
            //    else
            //    {
            //        isJvmRefer = false;
            //    }
            //}
            //else
            //{
            //    isJvmRefer = isInjvm().booleanValue();
            //}

            if (isJvmRefer)
            {
                URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, Interface).AddParameters(dict);
                invoker = refprotocol.Refer<T>(url);
                //if (logger.isInfoEnabled())
                //{
                //    logger.info("Using injvm service " + interfaceClass.getName());
                //}
            }
            else
            {
                if (!string.IsNullOrEmpty(Url))
                {
                    // 用户指定URL，指定的URL可能是对点对直连地址，也可能是注册中心URL
                    string[] us = Constants.SEMICOLON_SPLIT_PATTERN.Split(Url);
                    if (us != null && us.Length > 0)
                    {
                        foreach (string u in us)
                        {
                            URL url = URL.ValueOf(u);
                            if (string.IsNullOrEmpty(url.Path))
                            {
                                url = url.SetPath(Interface);
                            }
                            if (Constants.REGISTRY_PROTOCOL == url.Protocol)
                            {
                                //urls.Add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                            }
                            else
                            {
                                //urls.Add(ClusterUtils.mergeUrl(url, map));
                                urls.Add(url);
                            }
                        }
                    }
                }
                else
                {
                    // 通过注册中心配置拼装URL
                    //List<URL> us = loadRegistries(false);
                    //if (us != null && us.size() > 0)
                    //{
                    //    for (URL u : us)
                    //    {
                    //        URL monitorUrl = loadMonitor(u);
                    //        if (monitorUrl != null)
                    //        {
                    //            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                    //        }
                    //        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    //    }
                    //}
                    //if (urls == null || urls.size() == 0)
                    //{
                    //    throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                    //}
                }

                
                if (urls.Count == 1)
                {
                    refprotocol = ExtensionLoader.GetExtension<IProtocol>(urls[0].Protocol);
                    invoker = refprotocol.Refer<T>(urls[0]);
                }
                else
                {
                    List<IInvoker> invokers = new List<IInvoker>();
                    URL registryURL = null;
                    foreach (URL url in urls)
                    {
                        invokers.Add(refprotocol.Refer<T>(url));
                        if (Constants.REGISTRY_PROTOCOL == url.Protocol)
                        {
                            registryURL = url; // 用了最后一个registry url
                        }
                    }
                    if (registryURL != null)
                    { 
                        // 有 注册中心协议的URL
                        // 对有注册中心的Cluster 只用 AvailableCluster
                        //URL u = registryURL.AddParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                        //invoker = cluster.join(new StaticDirectory(u, invokers));
                    }
                    else
                    {
                        // 不是 注册中心的URL
                        //invoker = cluster.join(new StaticDirectory(invokers));
                    }
                }
            }

            var c = IsCheck;
            //if (c == null && consumer != null)
            //{
            //    c = consumer.isCheck();
            //}
            if (c == null)
            {
                c = true; // default true
            }
            if (c.Value && !invoker.IsAvailable)
            {
                throw new Exception("Failed to check the status of the service " + interfaceName + ". No provider available for the service "
                    + (Group == null ? "" : Group + "/") + interfaceName + (Version == null ? "" : ":" + Version) 
                    + " from the url " + invoker.Url + " to the consumer " 
                    //+ NetUtils.GetLocalHost()
                    + " use dubbo version " + Version);
            }
            //if (logger.isInfoEnabled())
            //{
            //    logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
            //}
            // 创建服务代理
            proxyFactory = ExtensionLoader.GetExtension<IProxyFactory>("transparent");
            return proxyFactory.GetProxy(invoker);
        }
    }
}
